﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace VideoClipNameHelper
{
    public class ReplayGain
    {
        //
        //http://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification
        //http://stackoverflow.com/questions/12639596/convert-replaygain-values-to-volume-adjustments
        //
        //REPLAYGAIN_GAIND‡€
        private static byte [] RG = { 0x52, 0x45, 0x50, 0x4C, 0x41, 0x59, 0x47, 0x41, 0x49, 0x4E, 0x5F, 0x47, 0x41, 0x49, 0x4E, 0x44, 0x87, 0x88 };
        // Uwaga: ostatnie dwa bajty bywają różne

        //REPLAYGAIN_PEAKD‡€
        private static byte[] RP = { 0x52, 0x45, 0x50, 0x4C, 0x41, 0x59, 0x47, 0x41, 0x49, 0x4E, 0x5F, 0x50, 0x45, 0x41, 0x4B, 0x44, 0x87, 0x88 };


        private static bool findGainInBuffer(byte[] bb, out double gainValue)
        {
            bool gainFound = false;
            //
            bool tagFound = false;
            int tagBegin = 0;
            for (int n = 0; n < (bb.Length - RG.Length); n++)
            {
                //if (RG[0] == bb[n + 0] && RG[1] == bb[n + 1] && RG[2] == bb[n + 2] && RG[3] == bb[n + 3] && RG[4] == bb[n + 4] && RG[5] == bb[n + 5] && RG[6] == bb[n + 6] && RG[7] == bb[n + 7] && RG[8] == bb[n + 8] && RG[9] == bb[n + 9] && RG[10] == bb[n + 10] && RG[11] == bb[n + 11] && RG[12] == bb[n + 12] && RG[13] == bb[n + 13] && RG[14] == bb[n + 14] && RG[15] == bb[n + 15] && RG[16] == bb[n + 16] && RG[17] == bb[n + 17] && RG[18] == bb[n + 18])
                    if (RG[0] == bb[n + 0] && RG[1] == bb[n + 1] && RG[2] == bb[n + 2] && RG[3] == bb[n + 3] && RG[4] == bb[n + 4] && RG[5] == bb[n + 5] && RG[6] == bb[n + 6] && RG[7] == bb[n + 7] && RG[8] == bb[n + 8] && RG[9] == bb[n + 9] && RG[10] == bb[n + 10] && RG[11] == bb[n + 11] && RG[12] == bb[n + 12] && RG[13] == bb[n + 13] && RG[14] == bb[n + 14] && RG[15] == bb[n + 15] && RG[16] == bb[n + 16])
                {
                    tagFound = true;
                    tagBegin = n;
                    break;

                }
            }
            //
            if (!tagFound)
            {
                gainValue = 0.0;
                return false;
            }
            //
            //
            int gainBegin = tagBegin + RG.Length;
            string gainValueString = "";
            gainValue = 0.0;
            //
            for (int j = gainBegin; j < bb.Length - RG.Length; j++)
            {
                char c = (char)bb[j];
                if (char.IsDigit(c) || '-' == c || '+' == c || '.' == c)
                {
                    gainValueString += c;
                }
                else
                {
                    gainValue = double.Parse(gainValueString, System.Globalization.CultureInfo.InvariantCulture);
                    gainFound = true;
                    break;
                }
            }
            //
            return gainFound;
        }
        //
        //
        public static bool ReadGain(string fileName, out double gainValue)
        {
            //
            const int bufferSize = 1500;
            //
            gainValue = 0.0;
            //
            try
            {
                using (System.IO.FileStream fileStream = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    byte[] bb = new byte[bufferSize];
                    //
                    if(fileStream.Length <= bufferSize)
                    {
                        return false;
                    }
                    //
                    // Foobar2000 umieszcza ReplayGain w plikach MKV na końcu
                    // ale trafiły mi się takie, które miały go umieszczone na początku
                    //
                    fileStream.Seek(-bb.Length, System.IO.SeekOrigin.End);
                    fileStream.Read(bb, 0, bb.Length);
                    //
                    if(findGainInBuffer(bb, out gainValue))
                    {
                        return true;
                    }
                    //
                    //
                    fileStream.Seek(200, System.IO.SeekOrigin.Begin);
                    fileStream.Read(bb, 0, bb.Length);
                    if (findGainInBuffer(bb, out gainValue))
                    {
                        return true;
                    }
                    //
                }
            }
            catch
            {
                return false;
            }
            //
            return false;
        }

        //
        //
        public static bool ReadGainSav(string fileName, out double gainValue)
        {
            //
            const int bufferSize = 1500;
            //
            bool gainFound = false;
            //
            try
            {
                using (System.IO.FileStream fileStream = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    byte[] bb = new byte[bufferSize];

                    fileStream.Seek(-bb.Length, System.IO.SeekOrigin.End);
                    fileStream.Read(bb, 0, bb.Length);

                    bool tagFound = false;
                    int tagBegin = 0;
                    for (int i = 0; i < bb.Length - 5; i++)
                    {
                        if (0x12 == bb[i] && 0x54 == bb[i + 1] && 0xc3 == bb[i + 2] && 0x67 == bb[i + 3])
                        {
                            tagFound = true;
                            tagBegin = i;
                            break;
                        }
                    }
                    //
                    if (!tagFound)
                    {
                        gainValue = 0.0;
                        return false;
                    }
                    //
                    int gainBegin = 0;
                    string gainValueString = "";
                    gainValue = 0.0;
                    for (int i = tagBegin; i < bb.Length - 16; i++)
                    {
                        //if (0xa3 == bb[i + 0] && 0x8f == bb[i + 1] && 0x52 == bb[i + 2] && 0x45 == bb[i + 3] && 0x50 == bb[i + 4] && 0x4c == bb[i + 5] && 0x41 == bb[i + 6] && 0x59 == bb[i + 7] && 0x47 == bb[i + 8] && 0x41 == bb[i + 9] && 0x49 == bb[i + 10] && 0x4e == bb[i + 11] && 0x5f == bb[i + 12] && 0x47 == bb[i + 13] && 0x41 == bb[i + 14] && 0x49 == bb[i + 15] && 0x4e == bb[i + 16] && 0x44 == bb[i + 17] && 0x87 == bb[i + 18] && 0x88 == bb[i + 19])
                        if (0xa3 == bb[i + 0] && 0x8f == bb[i + 1] && 0x52 == bb[i + 2] && 0x45 == bb[i + 3] && 0x50 == bb[i + 4] && 0x4c == bb[i + 5] && 0x41 == bb[i + 6] && 0x59 == bb[i + 7] && 0x47 == bb[i + 8] && 0x41 == bb[i + 9] && 0x49 == bb[i + 10] && 0x4e == bb[i + 11] && 0x5f == bb[i + 12] && 0x47 == bb[i + 13] && 0x41 == bb[i + 14] && 0x49 == bb[i + 15] && 0x4e == bb[i + 16] && 0x44 == bb[i + 17] && 0x87 == bb[i + 18])
                        {
                            gainFound = true;
                            gainBegin = i;
                            //
                            for (int j = gainBegin + 20; j < bb.Length - 20; j++)
                            {
                                char c = (char)bb[j];
                                if (char.IsDigit(c) || '-' == c || '+' == c || '.' == c)
                                {
                                    gainValueString += c;
                                }
                                else
                                {
                                    gainValue = double.Parse(gainValueString, System.Globalization.CultureInfo.InvariantCulture);
                                    break;
                                }
                            }
                            //
                            break;
                        }
                    }
                    //
                }
            }
            catch
            {
                gainValue = 0.0;
                return false;
            }
            //
            return gainFound;
        }

        public static bool ReadGain(string fileName, out double gainValue, out double peakValue)
        {
            //
            bool gainFound = false;
            bool peakFound = false;

            try
            {
                using (System.IO.FileStream fileStream = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    byte[] bb = new byte[1000];

                    fileStream.Seek(-bb.Length, System.IO.SeekOrigin.End);
                    fileStream.Read(bb, 0, bb.Length);

                    bool tagFound = false;
                    int tagBegin = 0;
                    for (int i = 0; i < bb.Length - 5; i++)
                    {
                        if (0x12 == bb[i] && 0x54 == bb[i + 1] && 0xc3 == bb[i + 2] && 0x67 == bb[i + 3])
                        {
                            tagFound = true;
                            tagBegin = i;
                            break;
                        }
                    }

                    if (!tagFound)
                    {
                        gainValue = 1.0;
                        peakValue = 0.0;
                        return false;
                    }

                    int gainBegin = 0;
                    string gainValueString = "";
                    gainValue = 0.0;
                    for (int i = tagBegin; i < bb.Length - 20; i++)
                    {
                        if (0xa3 == bb[i + 0] && 0x8f == bb[i + 1] && 0x52 == bb[i + 2] && 0x45 == bb[i + 3] && 0x50 == bb[i + 4] && 0x4c == bb[i + 5] && 0x41 == bb[i + 6] && 0x59 == bb[i + 7] && 0x47 == bb[i + 8] && 0x41 == bb[i + 9] && 0x49 == bb[i + 10] && 0x4e == bb[i + 11] && 0x5f == bb[i + 12] && 0x47 == bb[i + 13] && 0x41 == bb[i + 14] && 0x49 == bb[i + 15] && 0x4e == bb[i + 16] && 0x44 == bb[i + 17] && 0x87 == bb[i + 18] && 0x88 == bb[i + 19])
                        {
                            gainFound = true;
                            gainBegin = i;
                            //
                            for (int j = gainBegin + 20; j < bb.Length - 20; j++)
                            {
                                char c = (char)bb[j];
                                if (char.IsDigit(c) || '-' == c || '+' == c || '.' == c)
                                {
                                    gainValueString += c;
                                }
                                else
                                {
                                    gainValue = double.Parse(gainValueString, System.Globalization.CultureInfo.InvariantCulture);
                                    break;
                                }
                            }
                            //
                            break;
                        }
                    }
                    //
                    //
                    int peakBegin = 0;
                    string peakValueString = "";
                    peakValue = 0.0;
                    for (int i = tagBegin; i < bb.Length - 20; i++)
                    {
                        //
                        if (0xa3 == bb[i + 0] && 0x8f == bb[i + 1] && 0x52 == bb[i + 2] && 0x45 == bb[i + 3] && 0x50 == bb[i + 4] && 0x4c == bb[i + 5] && 0x41 == bb[i + 6] && 0x59 == bb[i + 7] && 0x47 == bb[i + 8] && 0x41 == bb[i + 9] && 0x49 == bb[i + 10] && 0x4e == bb[i + 11] && 0x5f == bb[i + 12] && 0x50 == bb[i + 13] && 0x45 == bb[i + 14] && 0x41 == bb[i + 15] && 0x4b == bb[i + 16] && 0x44 == bb[i + 17] && 0x87 == bb[i + 18] && 0x88 == bb[i + 19])
                        {
                            peakFound = true;
                            peakBegin = i;
                            //
                            for (int j = peakBegin + 20; j < bb.Length - 20; j++)
                            {
                                char c = (char)bb[j];
                                if (char.IsDigit(c) || '-' == c || '+' == c || '.' == c)
                                {
                                    peakValueString += c;
                                }
                                else
                                {
                                    peakValue = double.Parse(peakValueString, System.Globalization.CultureInfo.InvariantCulture);
                                    break;
                                }
                            }
                            //
                            break;
                        }
                    }
                    //
                    //
                    //volume = Math.Min(Math.Pow(10.0, gainValue / 20.0), 1 / peakValue);
                }
            }
            catch
            {
                gainValue = 1.0;
                peakValue = 0.0;
                return false;
            }
            //
            return gainFound;
        }

    }//class
}//namespace
